package algotrader;

import java.io.FileInputStream;

import quickfix.DefaultMessageFactory;
import quickfix.FieldNotFound;
import quickfix.FileStoreFactory;
import quickfix.Initiator;
import quickfix.MessageFactory;
import quickfix.MessageStoreFactory;
import quickfix.ScreenLogFactory;
import quickfix.SessionSettings;
import quickfix.SocketInitiator;
import quickfix.field.ClOrdID;
import quickfix.field.MaxFloor;
import quickfix.field.MinQty;
import quickfix.field.OrdType;
import quickfix.field.OrderID;
import quickfix.field.OrderQty;
import quickfix.field.OrigClOrdID;
import quickfix.field.Price;
import quickfix.field.Side;
import quickfix.field.Symbol;
import quickfix.field.TimeInForce;
import quickfix.fix42.ExecutionReport;
import quickfix.fix42.NewOrderSingle;
import quickfix.fix42.OrderCancelReplaceRequest;
import quickfix.fix42.OrderCancelRequest;
import algotrader.book.BookManager;
import algotrader.fakeserver.FakeFixServer;
import algotrader.fakeserver.FakeServerObserver;
import algotrader.gui.OrdersPanel;
import junit.framework.TestCase;

public class OrderSendingTestCase extends TestCase {
	
	private Initiator initiator;
	private FakeFixServer server;
	private FixApplication fixApp;
	private FakeServerObserver fakeServerObserver;
	private OperadorPregao operadorPregao;
	Order order;
	NewOrderSingle newOrder;
	
	protected void setUp() {
		/****************************
		 * Inicializando tanto o initiator do FixApp, quanto o FakeServer
		 ***************************/
		fakeServerObserver = new FakeServerObserver();
		server = new FakeFixServer();
		server.getFixApp().setObserver(fakeServerObserver);
		try {
			String fileName = "mdsTest.cfg";
			SessionSettings settings = new SessionSettings(new FileInputStream(
					fileName));

			MessageStoreFactory storeFactory = new FileStoreFactory(settings);
			MessageFactory messageFactory = new DefaultMessageFactory();
			ScreenLogFactory screenLog = new ScreenLogFactory(true, true, true);

			fixApp = new FixApplication(new BookManager());
			operadorPregao = new OperadorPregao(fixApp);
			operadorPregao.addObserverGeral(new OrdersPanel(operadorPregao));
			operadorPregao.addObserver("AMBV4",new OrdersPanel(operadorPregao));
			operadorPregao.addObserver("PETR4",new OrdersPanel(operadorPregao));
			initiator = new SocketInitiator(fixApp, storeFactory, settings,
					screenLog, messageFactory);
			initiator.start();
			int i = 0;
			while(!initiator.isLoggedOn() || i==0) {
				i=1;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void tearDown() {
		initiator.stop();
		server.stop();
	}
	
	private void aguardaLogon() {
		while(fixApp.getSessions().size()==0) {}
	}
	
	public void testOrderSending() {
		aguardaLogon();
		Order order;
		NewOrderSingle newOrder;
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("AMBV4", 1000, 34.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.DAY);
		operadorPregao.sendOrder(order);
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		verificaNewOrderSingle(order, newOrder);
		
		// Verificando ordem de compra Ltda, Validade EOC.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.EOC);
		order.setMinQty(100);
		order.setMaxFloor(100);
		operadorPregao.sendOrder(order);
		while(fakeServerObserver.getNewOrdersSingle().size()==1);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(1);
		verificaNewOrderSingle(order, newOrder);
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, Order.Side.BUY, Order.OrderType.MARKET, Order.Validade.DAY);
		operadorPregao.sendOrder(order);
		while(fakeServerObserver.getNewOrdersSingle().size()==2);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(2);
		verificaNewOrderSingle(order, newOrder);
		
		// Verificando ordem de compra stop limit, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.STOP_LIMIT, Order.Validade.DAY);
		order.setStopPrice(21.7);
		operadorPregao.sendOrder(order);
		while(fakeServerObserver.getNewOrdersSingle().size()==3);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(3);
		verificaNewOrderSingle(order, newOrder);
	}
	
	public void testOrderRegister() throws FieldNotFound {
		aguardaLogon();
		Order order;
		NewOrderSingle newOrder;
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.EOC);
		operadorPregao.sendOrder(order);
		
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		verificaNewOrderSingle(order, newOrder);
		
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.NOVA);
		
		server.getFixApp().registerOrder(newOrder);
		while(fixApp.getExecutions().size()==0); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.REGISTRADA);
		ExecutionReport exec = fixApp.getExecutions().get(0);
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getOrderID(),exec.get(new OrderID()).getValue());
	}

	public void testTotalOrderExecution() {
		registraOrdem();
		
		server.getFixApp().executeOrder(newOrder);
		while(fixApp.getExecutions().size()==1); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
	}
	
	public void testParcialOrderExecution() {
		aguardaLogon();
		Order order;
		NewOrderSingle newOrder;
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.EOC);
		operadorPregao.sendOrder(order);
		
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.NOVA);
		
		server.getFixApp().registerOrder(newOrder);
		while(fixApp.getExecutions().size()==0); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.REGISTRADA);
		
		server.getFixApp().parcialExecuteOrder(newOrder,100);
		while(fixApp.getExecutions().size()==1); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.PARCIALMENTE_EXECUTADA);
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getExecuted(),100.0);
		
		server.getFixApp().executeOrder(newOrder);
		while(fixApp.getExecutions().size()==2); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getExecuted(),700.0);
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
		
	}
	
	public void testTriggerAndExecutionStopLimitOrders() {
		aguardaLogon();
		Order order;
		NewOrderSingle newOrder;
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.STOP_LIMIT, Order.Validade.DAY);
		order.setStopPrice(21.9);
		
		operadorPregao.sendOrder(order);
		
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		
		server.getFixApp().registerOrder(newOrder);
		while(fixApp.getExecutions().size()==0); //esperando receber exec de ordem registrada.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.REGISTRADA);
		
		server.getFixApp().triggerStopLimitOrder(newOrder);
		while(fixApp.getExecutions().size()==1); //esperando receber exec de ordem ativada.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getType(),Order.OrderType.LIMIT);
		
		server.getFixApp().executeOrder(newOrder);
		while(fixApp.getExecutions().size()==2); //esperando receber exec de ordem executada.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
	}
	
	public void testCancelingExecutedOrder() {
		registraOrdem();
		
		server.getFixApp().executeOrder(newOrder);
		while(fixApp.getExecutions().size()==1); //esperando receber exec de ordem executada.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
		
		server.getFixApp().cancelaNegocio(newOrder);
		while(fixApp.getExecutions().size()==2); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.CANCELADA);
	}
	
	public void testDoneClientCancelOrder() throws FieldNotFound {
		registraOrdem();
		
		operadorPregao.cancelOrder(order);
		
		while(fakeServerObserver.getOrderCancelRequests().size()==0);
		OrderCancelRequest c = fakeServerObserver.getOrderCancelRequests().get(0);
		assertEquals(newOrder.get(new ClOrdID()).getValue(),c.get(new OrigClOrdID()).getValue());
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getOrderID(),c.get(new OrderID()).getValue());
		
		server.getFixApp().acceptCancelOrderRequest(newOrder);
		
		while(fixApp.getExecutions().size()==1); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.CANCELADA);
	}
	
	public void testNotDoneClientCancelOrder() {
		registraOrdem();
		
		server.getFixApp().executeOrder(newOrder);
		while(fixApp.getExecutions().size()==1); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
		
		operadorPregao.cancelOrder(order);
		
		while(fakeServerObserver.getOrderCancelRequests().size()==0);
		OrderCancelRequest c = fakeServerObserver.getOrderCancelRequests().get(0);
		
		server.getFixApp().rejectCancelOrderRequest(c);
		
		while(fixApp.getCancelOrderRejects().size()==0); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.EXECUTADA);
	}
	
	public void testAlterOrder() {
		registraOrdem();
		
		Order alterada = new Order(order.getSymbol(),order.getQty(),order.getSide(),order.getType(),order.getValidade());
		alterada.setId(order.getId());
		alterada.setOrderID(order.getOrderID());
		alterada.setQty(1000.0);
		alterada.setMaxFloor(200);
		alterada.setPrice(26.7);
		alterada.setTimeInForce(Order.Validade.EOC);
		alterada.setType(Order.OrderType.STOP_LIMIT);
		
		operadorPregao.askToAlterOrder(alterada);
		
		while(fakeServerObserver.getOrderCancelReplaceRequests().size()==0); //waiting server receiv the ask
		OrderCancelReplaceRequest rr = fakeServerObserver.getOrderCancelReplaceRequests().get(0);
		
		server.getFixApp().acceptReplace(rr);
		
		while(fixApp.getReplaces().size()==0);
		System.out.println("preco: " +operadorPregao.getOrder(order.getSymbol(),order.getId()).getPrice());
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getPrice()==26.7);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getQty()==1000);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getMaxFloor()==200);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getValidade()==Order.Validade.EOC);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getType()==Order.OrderType.STOP_LIMIT);
	}
	
	public void testAlterOrderNotAllowed() {
		registraOrdem();
		
		Order alterada = new Order(order.getSymbol(),order.getQty(),order.getSide(),order.getType(),order.getValidade());
		alterada.setId(order.getId());
		alterada.setOrderID(order.getOrderID());
		
		alterada.setQty(1000.0);
		alterada.setMaxFloor(200);
		alterada.setPrice(26.7);
		alterada.setTimeInForce(Order.Validade.EOC);
		alterada.setType(Order.OrderType.STOP_LIMIT);
		
		operadorPregao.askToAlterOrder(alterada);
		
		while(fakeServerObserver.getOrderCancelReplaceRequests().size()==0); //waiting server receiv the ask
		OrderCancelReplaceRequest rr = fakeServerObserver.getOrderCancelReplaceRequests().get(0);
		
		server.getFixApp().rejectReplace(rr);
		
		while(fixApp.getCancelOrderRejects().size()==0);
		System.out.println("preco: " +operadorPregao.getOrder(order.getSymbol(),order.getId()).getPrice());
		//assertTrue(operadorPregao.getOrder(order.getId()).getPrice()==21.5);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getQty()==700);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getMaxFloor()==0);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getValidade()==Order.Validade.DAY);
		assertTrue(operadorPregao.getOrder(order.getSymbol(),order.getId()).getType()==Order.OrderType.LIMIT);
	}
	
	public void testOrderRejecting() {
		aguardaLogon();
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.DAY);
		operadorPregao.sendOrder(order);
		
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.NOVA);
		
		server.getFixApp().rejectOrder(newOrder);
		while(fixApp.getExecutions().size()==0); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.REJEITADA);
		
		
	}

	private void registraOrdem() {
		aguardaLogon();
		
		// Verificando ordem de compra Ltda, Validade dia.
		order = new Order("PETR4", 700, 21.5, Order.Side.BUY, Order.OrderType.LIMIT, Order.Validade.DAY);
		operadorPregao.sendOrder(order);
		
		while(fakeServerObserver.getNewOrdersSingle().size()==0);
		newOrder = fakeServerObserver.getNewOrdersSingle().get(0);
		
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.NOVA);
		
		server.getFixApp().registerOrder(newOrder);
		while(fixApp.getExecutions().size()==0); //esperando receber exec.
		assertEquals(operadorPregao.getOrder(order.getSymbol(),order.getId()).getEstado(),Order.Estado.REGISTRADA);
	}
	
	private void verificaNewOrderSingle(Order compra, NewOrderSingle newCompra) {
		try {
			assertEquals(compra.getSymbol(),newCompra.get(new Symbol()).getValue());
			assertFalse(compra.getSymbol()==newCompra.get(new Symbol()).getValue());
			
			assertEquals(compra.getQty(),newCompra.get(new OrderQty()).getValue());
			
			if(compra.getType()!=Order.OrderType.MARKET)
				assertEquals(compra.getPrice(),newCompra.get(new Price()).getValue());
			else
				assertFalse(newCompra.isSet(new Price()));
			
			assertEquals(compra.getSide().getValue(),newCompra.get(new Side()).getValue());
			
			assertEquals(compra.getType().getValue(),newCompra.get(new OrdType()).getValue());
			
			assertEquals(compra.getValidade().getValue(),newCompra.get(new TimeInForce()).getValue());
			
			if(compra.getMinQty()>0) 
				assertEquals(compra.getMinQty(),newCompra.get(new MinQty()).getValue());
			else
				assertFalse(newCompra.isSet(new MinQty()));
			
			if(compra.getMaxFloor()>0) 
				assertEquals(compra.getMaxFloor(),newCompra.get(new MaxFloor()).getValue());
			else
				assertFalse(newCompra.isSet(new MaxFloor()));
			
		} catch (FieldNotFound e) {
			e.printStackTrace();
		}
	}
}
